﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Windows.Forms;
using System.Collections;

using Cards.GUI;
using Cards.Util;

namespace Cards.Objects
{
    /// <summary>
    /// Multi game client class
    /// </summary>
    public class GameClient : MultiGame
    {
        public AsyncCallback m_pfnCallBack; /// Thread to listen asynchronously call back
        public Socket m_clientSocket;       /// Client socket

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="_parentForm"></param>
        public GameClient(FrmCreateMultiGame _parentForm) : base(_parentForm)
        {
        }

        /// <summary>
        /// Start connect
        /// </summary>
        /// <returns></returns>
        public override Boolean Start()
        {
            return Connect();
        }

        /// <summary>
        /// Disconnect
        /// </summary>
        public override void Stop()
        {
            Disconnect();
        }

        /// <summary>
        /// Connect to server
        /// </summary>
        /// <returns></returns>
        public Boolean Connect()
        {
            if (m_port == "" || m_IP == "")
            {
                MessageBox.Show("IP Address and Port Number are required to connect to the Server\n");
                return false;
            }

            try
            {
                // Create the socket instance
                m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                // Cet the remote IP address
                IPAddress ip = IPAddress.Parse(m_IP);
                int iPortNo = System.Convert.ToInt16(m_port);

                // Create the end point 
                IPEndPoint ipEnd = new IPEndPoint(ip, iPortNo);

                // Connect to the remote host
                m_clientSocket.Connect(ipEnd);
                if (m_clientSocket.Connected)
                {
                    //Wait for data asynchronously 
                    WaitForData();

                    String msg = NetworkConstants.MESSAGETYPE_PLAYERNAME 
                                + NetworkConstants.MESSAGETYPE_SEPARATOR
                                + MainFrame.s_loginName;
                    SendMessage(msg);

                    return true;
                }

                return false;
            }
            catch (SocketException se)
            {
                string str;
                str = "\nConnection failed, is the server running?\n" + se.Message;
                //MessageBox.Show(str);
                m_frmParentMultiGame.UpdateMsg(str);
                return false;
            }
        }

        /// <summary>
        /// Start to waiting data from server
        /// </summary>
        public void WaitForData()
        {
            try
            {
                if (m_pfnCallBack == null)
                {
                    m_pfnCallBack = new AsyncCallback(OnDataReceived);
                }
                SocketPacket theSocPkt = new SocketPacket();
                theSocPkt.m_currentSocket = m_clientSocket;

                // Start listening to the data asynchronously
                IAsyncResult result = m_clientSocket.BeginReceive(theSocPkt.m_dataBuffer,
                                                        0, theSocPkt.m_dataBuffer.Length,
                                                        SocketFlags.None,
                                                        m_pfnCallBack,
                                                        theSocPkt);
            }
            catch (SocketException se)
            {
                //MessageBox.Show(se.Message);
                m_frmParentMultiGame.UpdateMsg(se.Message);
            }
        }

        /// <summary>
        /// Thread manage data received from server
        /// </summary>
        /// <param name="asyn"></param>
        public void OnDataReceived(IAsyncResult asyn)
        {
            try
            {
                SocketPacket theSockId = (SocketPacket)asyn.AsyncState;
                int iRx = theSockId.m_currentSocket.EndReceive(asyn);
                char[] chars = new char[iRx];

                System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
                int charLen = d.GetChars(theSockId.m_dataBuffer, 0, iRx, chars, 0);

                System.String szData = new System.String(chars);

                //testTxt = testTxt + szData;
                //m_frmParentMultiGame.UpdateTestText(String.Format("Received from server: {0} \n", szData));

                ProcessMessage(szData);

                WaitForData();
            }
            catch (ObjectDisposedException)
            {
                System.Diagnostics.Debugger.Log(0, "1", "\nOnDataReceived: Socket has been closed\n");
                //m_frmParentMultiGame.UpdateMsg("Máy chủ trò chơi đã dừng chơi");
            }
            catch (SocketException se)
            {
                //MessageBox.Show(se.Message);
                m_frmParentMultiGame.UpdateMsg(se.Message);
                //m_frmParentMultiGame.UpdateMsg("Máy chủ đã bị ngắt");
            }
        }

        /// <summary>
        /// Treat with message from server
        /// </summary>
        /// <param name="_message"></param>
        public void ProcessMessage(String _message)
        {
            String[] msgList = _message.Split(NetworkConstants.MESSAGETYPE_SEPARATOR.ToCharArray());
            String command = msgList[0];
            int idx = 0;

            if (msgList.Length <= 0)
            {
                return;
            }

            switch (command)
            {
                case NetworkConstants.MESSAGETYPE_SERVER_WELCOME:
                    m_frmParentMultiGame.OnConnectGameSucceed();

                    idx++;
                    m_frmParentMultiGame.GAMEPLAYERID = Int32.Parse(msgList[idx]);

                    break;

                case NetworkConstants.MESSAGETYPE_SERVER_BROADCAST_PLAYER_LIST:
                    ArrayList listPlayerStruct = new ArrayList();
                    for (idx = 1; idx < msgList.Length; idx++)
                    {
                        String subStr = msgList[idx];
                        String[] subMsgList = subStr.Split(NetworkConstants.MESSAGETYPE_SUBSEPARATOR.ToCharArray());

                        PlayerStruct st = new PlayerStruct();
                        st.m_id = Int32.Parse(subMsgList[0]);
                        st.m_name = subMsgList[1];
                        st.m_playerType = (PLAYER_TYPE)(Int32.Parse(subMsgList[2]));
                        listPlayerStruct.Add(st);
                    }

                    m_frmParentMultiGame.OnReceivePlayerList(listPlayerStruct);
                    break;

                case NetworkConstants.MESSAGETYPE_SERVER_START_GAME:
                    int[] arrCards = new int[Constants.s_TotalOfCards];
                    idx++;
                    String[] strCards = msgList[idx].Split(NetworkConstants.MESSAGETYPE_SUBSEPARATOR.ToCharArray());
                    for (int i = 0; i < strCards.Length; i++)
                    {
                        arrCards[i] = Int32.Parse(strCards[i]);
                    }

                    idx++;
                    int mIndex = Int32.Parse(msgList[idx]);

                    m_frmParentMultiGame.StartGame(arrCards, mIndex);

                    break;

                case NetworkConstants.MESSAGETYPE_SERVER_CONTINUE_GAME:
                    arrCards = new int[Constants.s_TotalOfCards];
                    idx++;
                    strCards = msgList[idx].Split(NetworkConstants.MESSAGETYPE_SUBSEPARATOR.ToCharArray());
                    for (int i = 0; i < strCards.Length; i++)
                    {
                        arrCards[i] = Int32.Parse(strCards[i]);
                    }

                    idx++;
                    mIndex = Int32.Parse(msgList[idx]);

                    m_frmParentMultiGame.ContinueGame(arrCards, mIndex);

                    break;

                case NetworkConstants.MESSAGETYPE_MOVE:
                    int id;
                    idx++;
                    id = Int32.Parse(msgList[idx]);

                    idx++;
                    strCards = msgList[idx].Split(NetworkConstants.MESSAGETYPE_SUBSEPARATOR.ToCharArray());

                    arrCards = new int[strCards.Length];

                    for (int i = 0; i < strCards.Length; i++)
                    {
                        arrCards[i] = Int32.Parse(strCards[i]);
                    }

                    m_frmParentMultiGame.OnReceiveMove(id, arrCards, false);

                    break;

                case NetworkConstants.MESSAGETYPE_SKIP:
                    idx++;
                    id = Int32.Parse(msgList[idx]);

                    m_frmParentMultiGame.OnReceiveSkip(id, false);

                    break;

                default:
                    break;
            }
        }

        /// <summary>
        /// Send a message to server
        /// </summary>
        /// <param name="_str"></param>
        public override void SendMessage(String _str)
        {
            try
            {
                int res = 0;
                Object objData = _str;
                byte[] byData = System.Text.Encoding.ASCII.GetBytes(objData.ToString());

                if (m_clientSocket != null)
                {
                    res = m_clientSocket.Send(byData);
                }
            }
            catch (SocketException se)
            {
                m_frmParentMultiGame.UpdateMsg(se.Message);
            }	
        }

        /// <summary>
        /// Disconnect from server
        /// </summary>
        public void Disconnect()
        {
            if ( m_clientSocket != null )
			{
				m_clientSocket.Close ();
				m_clientSocket = null;
			}
        }

        #region Game actions

        /// <summary>
        /// Generic send command
        /// </summary>
        /// <param name="_command"></param>
        /// <param name="_message"></param>
        public override void SendCommand(String _command, String _message, int _id = -1)
        {
            String msg = _command + NetworkConstants.MESSAGETYPE_SEPARATOR;
            msg += _message;

            SendMessage(msg);
        }

        /// <summary>
        /// Begin playing game
        /// </summary>
        /// <param name="_message"></param>
        public override void BeginGame(String _message)
        {
        }

        /// <summary>
        /// Continue game
        /// </summary>
        /// <param name="_message"></param>
        public override void ContinueGame(String _message)
        {
            SendCommand(NetworkConstants.MESSAGETYPE_SERVER_CONTINUE_GAME, _message);
        }

        /// <summary>
        /// Send a move to server to broadcast
        /// </summary>
        /// <param name="_message"></param>
        public override void SendAMove(String _message, int _id = -1)
        {
            SendCommand(NetworkConstants.MESSAGETYPE_MOVE, _message);
        }

        public override void SendASkip(String _message, int _id = -1)
        {
            SendCommand(NetworkConstants.MESSAGETYPE_SKIP, _message);
        }

        #endregion Game actions
    }
}
